package cn.ddiancan.auth.config;

import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.UUID;

import javax.sql.DataSource;

import cn.ddiancan.auth.service.enhancer.XddCustomTokenEnhancer;
import cn.ddiancan.auth.service.repository.XddJdbcOAuth2AuthorizationService;
import cn.ddiancan.auth.service.repository.XddJdbcRegisteredClientRepository;
import cn.ddiancan.auth.service.userdetails.XddcloudUserDetailsImpl;
import cn.ddiancan.xddcloud.common.context.XddCloudContextUtils;
import cn.ddiancan.xddcloud.ddccore.cache.XddCloudCacheClient;
import cn.ddiancan.xddcloud.psc.base.service.UserClientService;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;

@Configuration
@EnableConfigurationProperties(OAuth2ClientProperties.class)
public class XddcloudAuthConfiguration {

    @Autowired
    private OAuth2ClientProperties oAuth2ClientProperties;

    @Bean
    public XddcloudUserDetailsImpl userDetailsService(UserClientService userClientService,
        XddCloudCacheClient xddCloudCacheClient) {
        return new XddcloudUserDetailsImpl(userClientService, xddCloudCacheClient);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    @Bean
    public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
        Instant now = Instant.now();
        RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
            .clientId(oAuth2ClientProperties.getRegistration().get(XddCloudContextUtils.getApplication()).getClientId())
            .clientSecret(
                oAuth2ClientProperties.getRegistration().get(XddCloudContextUtils.getApplication()).getClientSecret())
            .clientName(XddCloudContextUtils.getApplication()).clientIdIssuedAt(now)
            .clientSecretExpiresAt(now.plus(60, ChronoUnit.DAYS)).clientAuthenticationMethods(authMethods -> {
                authMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_POST);
                authMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);
            }).scopes(scopeSet -> scopeSet.addAll(
                oAuth2ClientProperties.getRegistration().get(XddCloudContextUtils.getApplication()).getScope()))
            .authorizationGrantType(new AuthorizationGrantType(
                oAuth2ClientProperties.getRegistration().get(XddCloudContextUtils.getApplication())
                    .getAuthorizationGrantType()))
            .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).requireProofKey(false).build())
            .tokenSettings(TokenSettings.builder().accessTokenFormat(OAuth2TokenFormat.SELF_CONTAINED)
                .idTokenSignatureAlgorithm(SignatureAlgorithm.RS256).accessTokenTimeToLive(Duration.ofHours(4))
                .refreshTokenTimeToLive(Duration.ofHours(6))
                .reuseRefreshTokens(false).build()).build();
        JdbcRegisteredClientRepository jdbcRegisteredClientRepository =
            new XddJdbcRegisteredClientRepository(jdbcTemplate);
        jdbcRegisteredClientRepository.save(registeredClient);
        return jdbcRegisteredClientRepository;
    }

    @Bean
    public OAuth2AuthorizationService oAuth2AuthorizationService(JdbcTemplate jdbcTemplate,
        RegisteredClientRepository registeredClientRepository) {
        return new XddJdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
    }

    @Bean
    public AuthorizationServerSettings authorizationServerSettings() {
        return AuthorizationServerSettings.builder().build();
    }

    @Bean
    public XddCustomTokenEnhancer customTokenEnhancer() {
        return new XddCustomTokenEnhancer();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource() {
        return new DruidDataSource();
    }
}
